package com.pudu.pudusclient;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Message;
import android.util.Log;

import com.pudu.pdrobot.App;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.lang.ref.WeakReference;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;

public class ControlServiceService extends Service {

    public static boolean mServiceRunning = false;
    MyHandler mHandler;

    @Override
    public IBinder onBind(Intent intent) {
        Log.i(App.MYTAG, "ControlServiceService.onBind");
        return binder;
    }


    respondCallback mCallback;

    public static interface respondCallback {
        public byte[] OnReceive(byte[] data);
    }

    public void SetReceiveCallback(respondCallback callback) {
        mCallback = callback;
    }

    canCallback mCANCallback;

    public static interface canCallback {
        public byte[] OnReceive(byte[] data);
    }

    public void setcanCallback(canCallback callback) {
        mCANCallback = callback;
    }

    @Override
    public void onCreate() {
        Log.i(App.MYTAG, "ControlServiceService.onCreate");

        StartSocketWork();
    }

    private static class MyHandler extends Handler {
        private final WeakReference<ControlServiceService> mServices;

        public MyHandler(ControlServiceService service) {
            mServices = new WeakReference<ControlServiceService>(service);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case CANDataService.MESSAGE_FROM_CAN_SPI:
                    //do your work
                    byte [] data = (byte[]) msg.obj;
                    try{
                        if( mServices != null && ControlServiceService.mServiceRunning == true)
                        {
                            if( data != null && mServices.get().mCANCallback != null ) {
                                //CAN_Bus接收的数据在界面显示
                                mServices.get().mCANCallback.OnReceive(data);
                            }
                        }
                    }
                    catch(Exception e)
                    {
                        e.printStackTrace();
                    }
                    break;
                default:
                    break;
            }
        }
    }

    public void SendDataToClient(byte[] data) {
        synchronized (mapClientSockets) {
            for (Map.Entry<Socket, ClientSocketData> entry : mapClientSockets.entrySet()) {
                Socket key = entry.getKey();
                ClientSocketData value = entry.getValue();
                value.mSendrunnable.enqueueSocketPacket(new MySocketPacket(data, 0x02));
            }
        }
    }

    public void StopSocketWork()
    {
        mServiceRunning = false;

        App.instance.canbusService.setHandler(null);
        App.instance.canbusService.StopCanDataHandle();

        try {
            server.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void StartSocketWork()
    {
        mServiceRunning = true;

        mHandler = new MyHandler(this);

        StartControlServer();
        if( App.instance.canbusService != null) {
            App.instance.canbusService.CANDataHandleStart();
            App.instance.canbusService.setHandler(mHandler);
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return Service.START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        Log.i(App.MYTAG, "ControlServiceService.onDestroy");
        super.onDestroy();
        StopSocketWork();
    }

    private IBinder binder = new ServiceBinder();

    public class ServiceBinder extends Binder {
        public ControlServiceService getService() {
            return ControlServiceService.this;
        }
    }

    public void RemoveClientSocketFromHashmap(ClientSocketData data) {
        synchronized (mapClientSockets) {
            mapClientSockets.remove(data.mClientsocket);
            byte[] byteCommand = {0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
            try{
                App.instance.canbusService.write(byteCommand);
            }
            catch(Exception e)
            {
                e.printStackTrace();
            }
        }
    }

    public static int SERVERPORT = 12345;
    private ServerSocket server = null;

    public void StartControlServer() {
        try {
            server = new ServerSocket(SERVERPORT);

            new Thread(new Runnable() {
                @Override
                public void run() {
                    while (true) {
                        try {
                            Socket client = server.accept();

                            client.setSoTimeout(1000);
                            MyCallback mycallback = new MyCallback();
                            HandlerThread clientThread = new HandlerThread("ClientSocketThread" + client);
                            clientThread.start();
                            Handler handler = new Handler(clientThread.getLooper(), mycallback);

                            Runnable Receiverunnale = new ReceiveRunnable(client);
                            Thread receive = new Thread(Receiverunnale);
                            receive.start();
                            Runnable Sendrunnale = new SendRunnable(client);
                            Thread send = new Thread(Sendrunnale);
                            send.start();

                            ClientSocketData data = new ClientSocketData(handler, client, clientThread,
                                    receive, send, Receiverunnale, Sendrunnale);
                            synchronized (mapClientSockets) {
                                mapClientSockets.put(client, data);
                            }
                        } catch (IOException e) {
                            Log.i("aaaa", "listen IOException");

                            e.printStackTrace();
                            break;
                        }
                    }
                }
            }).start();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    class ReceiveRunnable implements Runnable {
        Socket mSocket;
        public ClientSocketData mOwner;

        ReceiveRunnable(Socket socket) {
            mSocket = socket;
        }

        @Override
        public void run() {

            InputStream inputReader = null;

            while (mSocket.isConnected() && !Thread.interrupted()) {
                byte[] result = new byte[8];
                try {
                    inputReader = mSocket.getInputStream();
                    int len = inputReader.read(result);
                    //WIFI接收的数据在界面显示
                    if( result!= null && len == 8  ) {
                        if( mCallback != null) {
                            mCallback.OnReceive(result);
                        }
                    }

                    if (result!= null && len == 8) {
                        synchronized (App.instance.canbusService) {
                            if( App.instance.canbusService!= null && App.instance.canbusService.mCanDataHandle != null )
                            {
                                App.instance.canbusService.mCanDataHandle.dataFromClient(result);
                            }
                        }
                    }
                } catch (SocketTimeoutException e) {
                    continue;
                } catch (Exception e) {
                    e.printStackTrace();
                    break;
                }
            }

            Message msg = mOwner.mHandler.obtainMessage();
            msg.what = 0x02;
            msg.obj = mOwner;
            mOwner.mHandler.sendMessage(msg);

            Log.i("aaaa", "ReceiveRunnable quit");
        }
    }

    class SendRunnable implements Runnable {
        Socket mSocket;
        public ClientSocketData mOwner;

        private LinkedBlockingQueue<MySocketPacket> sendingPacketQueue;

        SendRunnable(Socket socket) {
            mSocket = socket;
        }

        public void enqueueSocketPacket(final MySocketPacket socketPacket) {
            try {
                getSendingPacketQueue().put(socketPacket);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        protected LinkedBlockingQueue<MySocketPacket> getSendingPacketQueue() {
            if (sendingPacketQueue == null) {
                sendingPacketQueue = new LinkedBlockingQueue<MySocketPacket>();
            }
            return sendingPacketQueue;
        }

        @Override
        public void run() {

            MySocketPacket packet;
            try {
                while (!Thread.interrupted() && mSocket.isConnected() &&
                        (packet = getSendingPacketQueue().take()) != null) {

                    InetAddress address = mSocket.getInetAddress();

//                    if(packet.getType() == 0x1) {
//                        if (address.isReachable(2000) == true) {
//                            Log.i("aaaa", "isReachable true. ");
//                        } else {
//                            Log.i("aaaa", "isReachable fail. ");
//                            throw new Exception("isReachable failed");
//                        }
//                    }

                    {
                        OutputStream out = mSocket.getOutputStream();
                        out.write(packet.getData());
                        out.flush();
                        Writer myout = new OutputStreamWriter(out);
                        myout.flush();
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

            Message msg = mOwner.mHandler.obtainMessage();
            msg.what = 0x02;
            msg.obj = mOwner;
            mOwner.mHandler.sendMessage(msg);

            Log.i("aaaa", "SendRunnable quit");
        }
    }

    Map<Socket, ClientSocketData> mapClientSockets = new HashMap<Socket, ClientSocketData>();

    public class ClientSocketData extends Object {
        Handler mHandler;
        Socket mClientsocket;
        HandlerThread mHandlethread;
        Thread mSendThread;
        Thread mReceiveThread;
        SendRunnable mSendrunnable;
        ReceiveRunnable mReceiverunnable;
        public boolean bWorking = true;

        ClientSocketData(Handler handler, Socket clientsocket, HandlerThread handlethread,
                         Thread receiveThread, Thread sendThread, Runnable receiverunnable, Runnable sendrunnable) {
            mHandler = handler;
            mClientsocket = clientsocket;
            mHandlethread = handlethread;
            mSendThread = sendThread;
            mReceiveThread = receiveThread;
            mSendrunnable = (SendRunnable) sendrunnable;
            mReceiverunnable = (ReceiveRunnable) receiverunnable;
            mSendrunnable.mOwner = this;
            mReceiverunnable.mOwner = this;
        }
    }

    public class MyCallback implements Handler.Callback {
        @Override
        public boolean handleMessage(Message msg) {

            switch (msg.what) {
                case 0x01:
                    ClientSocketData obj = (ClientSocketData) msg.obj;

                    obj.mSendrunnable.enqueueSocketPacket(new MySocketPacket("111".getBytes(), 0x1));

                    Message mymsg = obj.mHandler.obtainMessage();
                    mymsg.what = 0x01;
                    mymsg.obj = obj;

                    obj.mHandler.sendMessageDelayed(mymsg, 1000);
                    break;

                case 0x02:
                    ClientSocketData mydata = (ClientSocketData) msg.obj;
                    if (mydata.bWorking == false) {
                        return false;
                    }
                    mydata.mReceiveThread.interrupt();
                    mydata.mSendThread.interrupt();
                    try {
                        mydata.mClientsocket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                    mydata.mHandlethread.quit();
                    mydata.bWorking = false;

                    RemoveClientSocketFromHashmap(mydata);
                    break;
            }
            return false;
        }
    }


}
